﻿/// <reference path="../BaseObject.js" />
/// <reference path="_namespace.js" />
/// <reference path="../DomElements.js" />
/// <reference path="../io/Keyboard.js" />
/// <reference path="../IHaveDocument.js" />
/// <reference path="../drawing/Point.js"/>

/*****************************************************************************
Ajax library class definition. Kevin Robinson 2006.
Class: ajax.components.Component
/*****************************************************************************/


ajax.lang.addNamespace("ajax.components");

//-----------------------------------------------------------------------------
ajax.components.Component = function(doc) // public: constructor
{    
    ajax.components.Component.superConstructor.apply(this, arguments);
    this._id                = ""; 
    this.window             = ajax.lang.getDocumentWindow(doc);
    this.doc                = doc;               
    this.keyboard           = ajax.lang.safeNew("ajax.io.Keyboard");              
};
//-----------------------------------------------------------------------------
ajax.lang.extend("ajax.components.Component", "ajax.BaseObject"); // Inherit
ajax.lang.supports("ajax.components.Component", "ajax.IHaveDocument");
//-----------------------------------------------------------------------------

ajax.components.Component.prototype.get_id = function() { return this._id; }
ajax.components.Component.prototype.set_id = function(value) { this._id = value; }

ajax.components.Component.prototype.dispose = function()
{
    ajax.components.Component.superClass.dispose.call(this);
}

ajax.components.Component.prototype.getWindow = function()
{///<returns type="domWindow"/>        
    return this.window;
}

ajax.components.Component.prototype.getDocument = function()
{///<returns type="domDocument"/>
    return this.doc;
}

ajax.components.Component.prototype.getKeyboard = function()
{///<returns type="ajax.io.Keyboard"/>
    return this.keyboard;
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.loadCss = function(sPath) 
{///<summary>This method will ensure that the document has linked to an external stylesheet.</summary>
    // Check for existance
    var links = this.doc.getElementsByTagName("link");
    for (var i = 0; i < links.length; i++)
        if (links[i].getAttribute("rel") == "stylesheet" && links[i].getAttribute("href") == sPath)
            return; // Already exists
    
    // Create New
    var style = this.doc.createElement("link");
    style.rel = "stylesheet";
    style.href= sPath;
    this.doc.getElementsByTagName("head")[0].appendChild(style);              
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.injectCss = function(sPath)
{///<summary>This method will inject the content of a css stylesheet into the page (without linking).</summary>    
    // Check for existence
    var rc = ajax.lang.safeNew("ajax.lang.ResourceCache");
    var id = rc.createPathRef(sPath);
    if (this.doc.getElementById(id))
        return; // Already loaded
    
    // Get the file from the resource Cache
    var cssText = rc.getFile(sPath);
    
    // Create new style element with id of this css file
    var style   = this.doc.createElement("style");
    style.type  = "text/css";
    style.id    = id;
    if (style.styleSheet)   // IE
        style.styleSheet.cssText = cssText;
    else                    // w3c
    {
        var cssTextNode = this.doc.createTextNode(cssText);
        style.appendChild(cssTextNode);
    }
    // Add it to the document head
    this.doc.getElementsByTagName("head")[0].appendChild(style); 
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.loadContentToDiv = function(sHtml)
{///<summary>will load an HTML template into a hidden div.</summary>
    var loadingDiv = this.doc.createElement("div");
    loadingDiv.style.visibility = "hidden";
    loadingDiv.style.position   = "absolute";
    loadingDiv.style.left = loadingDiv.style.top = "0px";
    loadingDiv.innerHTML        = sHtml;
    this.doc.body.appendChild(loadingDiv);    
    return loadingDiv;
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.getEventLayerOffset = function(e, constrainedBy, bScrollOffset)
{///<returns type="ajax.drawing.Point"/>
    /*
    var result = {x: 0, y: 0};
    var parentEle   = e.srcElement || e.target;
    // because we are using the event.x (IE) we do NOT want scrolling taken into account.
    // as the event has already done that.
    var elePos      = this.getElementPosition(parentEle, constrainedBy, false);
    // layerX & Y already equals the "EventLayerOffset", at least for the immediate layer
    // since it ignores our constrainedBy object and will just use whatever layer is containing our ele.
    // Mostly that will be correct but it could cause BUGS.
    result.x        = elePos.x + e.offsetX || e.layerX;
    result.y        = elePos.y + e.offsetY || e.layerY;    
    return result;
    */
    
    
    // Get postions of event & Constrainer in relation to the doc body
    var elePos = {x: e.clientX, y: e.clientY};
    var parPos = this.getElementPosition(constrainedBy, this.doc.body, bScrollOffset);
    // Then return the offset of the child (the event position) from the parent
    var x = elePos.x - parPos.x;
    var y = elePos.y - parPos.y;
    return $new("ajax.drawing.Point", x, y);    
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.getElementLayerOffset  = function(parentEle, constrainedBy, bScrollOffset)
{///<summary>This method will get the offset of an element from a parentElement.</summary>
 ///<returns type="ajax.drawing.Point"/>
    return ajax.components.Component.getElementLayerOffset(this.doc, parentEle, constrainedBy, bScrollOffset);
}

//-----------------------------------------------------------------------------
ajax.components.Component.getElementLayerOffset = function(doc, parentEle, constrainedBy, bScrollOffset)
{///<summary>This method will get the offset of an element from a parentElement.</summary>
    ///<returns type="ajax.drawing.Point"/>
    
    // Get the the position of this ele relative to the doc body
    var elePos = ajax.components.Component.getElementPosition(doc, parentEle, doc.body, bScrollOffset);
    // get the position of the parent Ele relative to body
    var parPos = ajax.components.Component.getElementPosition(doc, constrainedBy, doc.body, bScrollOffset);
    
    // Return the offset of the child from the parent
    var x = elePos.x - parPos.x;
    var y = elePos.y - parPos.y;
    return $new("ajax.drawing.Point", x, y);   
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.getElementPosition = function(parentEle, constrainedBy, bScrollOffset)
{///<summary>Returns an object with the x, y offset of an object from a container.</summary>
 ///<returns type="ajax.drawing.Point"/>
	return ajax.components.Component.getElementPosition(this.doc, parentEle, constrainedBy, bScrollOffset);
}

//-----------------------------------------------------------------------------
ajax.components.Component.getElementPosition = function(doc, parentEle, constrainedBy, bScrollOffset)
{///<summary>Returns an object with the x, y offset of an object from a container.</summary>
 ///<returns type="ajax.drawing.Point"/>
    
    //default bScrollOffset to true
    bScrollOffset = (typeof(bScrollOffset) == "undefined") ? true : bScrollOffset;
    
	// No Event object so line the layer up with the parent element
	var x = 0;
	var y = 0;					

	// Now deal with elements inside tables and such, to work out our final x & y
	var parent = parentEle;
	while (parent) 
	{	    		    		       
        if ( parent == constrainedBy || 
            ajax.components.Component.nodeContains(parent, constrainedBy) ) // Reached or exceeded the parent node
		{
		    // Apply the parent Node...
		    x += constrainedBy.offsetLeft - (bScrollOffset ? constrainedBy.scrollLeft : 0);
		    y += constrainedBy.offsetTop  - (bScrollOffset ? constrainedBy.scrollTop : 0);
		    
		    // If the contrainedBy is the BODY tag, then take its *parent* scroll into consideration
		    // Since 
		    if (constrainedBy.tagName.toUpperCase() == "BODY")
		    {
		        x -= bScrollOffset ? constrainedBy.parentNode.scrollLeft : 0;
		        y -= bScrollOffset ? constrainedBy.parentNode.scrollTop : 0;
		    }
		    break;
		}
        
        x += parent.offsetLeft - (bScrollOffset ? parent.scrollLeft : 0);
		y += parent.offsetTop - (bScrollOffset ? parent.scrollTop : 0);
		parent = parent.offsetParent;
	}	
	
	// If we are being contained by an ie body and NOT in a frameset (top IS our parent)
	var t = top;
	
	if (constrainedBy == doc.body 
	    && typeof(doc.body.leftMargin) != "undefined"
	    && top == doc.parentWindow)
	{	    	    
	    // Special for IE offset from doc body	    
	    x -= parseInt(doc.body.leftMargin);
	    y -= parseInt(doc.body.topMargin);
	}

	return $new("ajax.drawing.Point", x, y);		
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.nodeContains = function(parent, child)
{///<returns type="boolean"/>
    return ajax.components.Component.nodeContains(parent, child);
}

//-----------------------------------------------------------------------------
ajax.components.Component.nodeContains = function(parent, child)
{///<returns type="boolean"/>
    for (var i = 0; i < parent.childNodes.length; i++)
    {	    	        
        if (parent.childNodes[i] == child)
            return true;	    
    }
    return false;
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.removeNode = function(node)
{///<returns type="boolean"/>
    return ajax.components.Component.removeNode(node);
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.createNamedElement = function(elementType, elementName)
{///<returns type="domElement"/>
    // Return from static method
    return ajax.components.Component.createNamedElement(this.doc, elementType, elementName);
}

ajax.components.Component.prototype.getElementScrollbarWidth = function(ele)
{///<returns type="number"/>
    return ajax.components.Component.getElementScrollbarWidth(ele);
}

//-----------------------------------------------------------------------------
ajax.components.Component.prototype.toString = function()
{
    return "ajax.components.Component";
}



//-----------------------------------------------------------------------------
// Statics
ajax.components.Component.getElementDoc = function(element)
{/// <retruns type="domDocument"/>
    return (element.ownerDocument || element.document || element);
}

ajax.components.Component.getElementWindow = function(element)
{/// <returns type="domWindow"/>
    var doc = ajax.components.Component.getElementDoc(element);
    return doc.defaultView || doc.parentWindow;
}

// wrapper to avoid ie secure server bug.
ajax.components.Component.removeNode = function(node)
{
    if (!node) // Nothing passed in
        return;

    if (ajax.Browser.isInternetExplorer && node.outerHTML && node.tagName != "TD" && node.tagName != "TR")
        node.outerHTML = ""; // ie
    else if (node.parentNode)
    {
        try
        {
            node.parentNode.removeChild(node); // w3c
        }
        catch (ex)
        {
            ajax.debug.Output.writeLn("unable to remove node: " + ex.message);
        }
    }
}

//-----------------------------------------------------------------------------
// Wrapper to create named elements in IE as well as wc3 browsers.
ajax.components.Component.createNamedElement = function(doc, elementType, elementName)
{///<returns type="domElement"/>
    var ele;    
    try
    {  
        // ie
        ele       = doc.createElement("<" + elementType + " name='" + elementName + "'>");     // create element       
    }
    catch(e)
    {
        //w3c
        ele       = doc.createElement(elementType);
        ele.name  = elementName;
    }
    return ele;
}

ajax.components.Component.getElementScrollbarWidth = function(ele)
{///<returns type="number"/>
    return 18; // TODO: is there any way to calculate this?
}

ajax.components.Component.cast = function(obj)
{///<returns type="ajax.components.Component"/>
    return obj;
}